#include "Arduino.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "zk_os.h"
#include "sqlite_demo.h"
#include "lua_app.h"
#include "sys_hal.h"
#include "ntp_time.h"
#include "hal_oled_ssd1327.h"
#include "hal_sdcard.h"
#include  "hal_pir.h"
#include "WiFi.h"

#define QUEUE_LENGTH 5
#define ITEM_SIZE sizeof(int)

ZKOS zkOS; 


static QueueHandle_t message_queue;
 
typedef struct {
    uint8_t *q_data;
    uint16_t q_data_len;
} message_data_t;
 
void msg_queue_send_task(void *pvParameters)
{
    printf("msg_queue_send_task\n");
    while(1)
    {
        uint8_t data_len = rand() % 256;
        message_data_t message_data;
 
        if(!data_len)
            data_len = 1;
 
        message_data.q_data_len = data_len;
        message_data.q_data = (uint8_t *)malloc(data_len);
        if (message_data.q_data == NULL) {
            printf("Malloc q_data_len failed!");
            return;
        }
        
        printf("[Send] message data len = %d\n",data_len);
        memset(message_data.q_data,data_len,data_len);
        if (xQueueSend(message_queue, (void *)&message_data, ( TickType_t ) 0) != pdTRUE) {
            printf("Failed to enqueue message_queue. Queue full.");
            /* If data sent successfully, then free the pointer in `xQueueReceive'
            * after processing it. Or else if enqueue in not successful, free it
            * here. */
            free(message_data.q_data);
        }
 
        vTaskDelay(1000 / portTICK_RATE_MS);
    }
    
}
 
void msq_queue_receive_task(void *pvParameters)
{
    printf("msq_queue_receive_task\n");
    while(1)
    {
        message_data_t message_data;
        if (xQueueReceive(message_queue, &message_data, portMAX_DELAY) != pdPASS) {
            printf("Queue receive error");
        } else {
            printf("[Receive 2] message_data len:%d\n",message_data.q_data_len);
            printf("[Receive 2] message_data[0]=%d message_data[%d]=%d\n",message_data.q_data[0],
                message_data.q_data_len-1,message_data.q_data[message_data.q_data_len-1]);
                free(message_data.q_data);
        }
    }
}   

uint64_t chipid;  
void system_info_task(void *pvParameters)
{
    while(1)
    {
        printf("Query system information....\n");
        chipid=ESP.getEfuseMac();//The chip ID is essentially its MAC address(length: 6 bytes).
        Serial.printf("ESP32 Chip ID = %04X",(uint16_t)(chipid>>32));//print High 2 bytes
        Serial.printf("%08X\n",(uint32_t)chipid);//print Low 4bytes. 
        Serial.printf("get Chip Cores = %u\n",ESP.getChipCores());
        Serial.printf("get Chip Revision = %u\n",ESP.getChipRevision());
        Serial.printf("get Chip Model = %u\n",ESP.getChipModel());

        Serial.printf("total heap size = %u KB \n",ESP.getHeapSize() / 1024);
        Serial.printf("available heap = %u KB \n",ESP.getFreeHeap() / 1024);
        Serial.printf("lowest level of free heap since boot = %u KB \n",ESP.getMinFreeHeap() / 1024);
        Serial.printf("largest block of heap that can be allocated at once = %u KB \n",ESP.getMaxAllocHeap() / 1024 );
        
        Serial.printf("total Psram size = %u KB \n",ESP.getPsramSize() / 1024 );
        Serial.printf("available Psram = %u KB \n",ESP.getFreePsram() / 1024 );
        Serial.printf("lowest level of free Psram since boot = %u KB \n",ESP.getMinFreePsram() / 1024 );
        Serial.printf("largest block of Psram that can be allocated at once = %u KB \n",ESP.getMinFreePsram() / 1024 );
        
 
        Serial.printf("getCpuFreqMHz = %u MHz\n",ESP.getCpuFreqMHz());
        Serial.printf("get Cycle Count = %u\n",ESP.getCycleCount());
        Serial.printf("get SdkVersion = %s\n",ESP.getSdkVersion());
        Serial.printf("get SketchSize = %u KB \n",ESP.getSketchSize() / 1024 );
        Serial.printf("get Free SketchSpace = %u KB \n",ESP.getFreeSketchSpace() / 1024);

        if(sysHal.getWifiType() == WIFI_AP ) {
            sysHal.wifiAPStatus();        
        }
        vTaskDelay(5000 / portTICK_RATE_MS);
    }
}

void sensor_task(void *pvParameters) {  
    while(1) {
        // Serial.printf("sensor_task\n");
        // read sensor data
        // send data to cloud
        // save data to local storage
        halPIR.update();
        vTaskDelay(1000 / portTICK_RATE_MS);
    }
}   


void lua_apps_task(void *pvParameters)  {
    Serial.printf("lua_apps_task\n");
    while(1) {
        //load lua script
        //execute lua script
        Serial.printf("cheking app status....\n");
        vTaskDelay(1000 / portTICK_RATE_MS);
    }
}

void init_task(void *pvParameters) {
    Serial.printf("init service...\n");
    while(1) {           
        halPIR.init();
        Serial.printf("init service done.\n");        
        vTaskDelete(NULL);
    }    
}
ZKOS::ZKOS() {
    Serial.printf("ZKOS初始化...\n");
}

ZKOS::~ZKOS() {
    Serial.printf("ZKOS释放...\n");
}

void ZKOS::init() {

    #ifdef MAIN_TASK_ENABLE
        message_data_t message_data;
        message_queue = xQueueCreate(15, sizeof(message_data_t));
        if (message_queue == NULL) {
            printf("Queue creation failed\n");
            return;
        }
        xTaskCreate(&lua_apps_task, "lua_apps_task", 1024 * 1024 * 100, NULL, 4, NULL);
        xTaskCreate(&msg_queue_send_task, "msg_queue_send_task", 2048, NULL, 6, NULL);
        xTaskCreate(&msq_queue_receive_task, "msq_queue_receive_task", 2048, NULL, 5, NULL);
        xTaskCreate(&system_info_task, "system_info_task", 2048, NULL, 4, NULL);
        xTaskCreate(&lua_apps_task, "lua_apps_task", 1024 * 1024 * 100, NULL, 4, NULL);
    #endif      

        // xTaskCreate(&init_task, "init_task", 1024 * 1024, NULL, 4, NULL);        
        xTaskCreate(&sensor_task, "sensor_task", 2048, NULL, 1, NULL);
        
    initHAL();
    // initDB();
    // initApps();
}

void ZKOS::initApps() {
    Serial.print("初始化APP...\n");
    // luaApp.init();
}

void ZKOS::initDB() {
    Serial.print("初始化存储系统...\n");
    // sqliteDemo.initSQLiteDemo();
}

void ZKOS::initHAL() {
    Serial.print("初始化硬件抽象层...\n");
    // halPIR.init();
    // sysHal.initWifimgr();
    // ntpTime.init();
    halOLEDSSD1327.init();
    halSDCard.init();
}